#include "Rp2WglApplication.h"
#include "Rp2WglRenderer.h"
using namespace Rp2;

const int WindowApplication::KEY_ESCAPE = VK_ESCAPE;
const int WindowApplication::KEY_LEFT_ARROW = VK_LEFT;
const int WindowApplication::KEY_RIGHT_ARROW = VK_RIGHT;
const int WindowApplication::KEY_UP_ARROW = VK_UP;
const int WindowApplication::KEY_DOWN_ARROW = VK_DOWN;
const unsigned char WindowApplication::KEY_W = 'w';
const unsigned char WindowApplication::KEY_A = 'a';
const unsigned char WindowApplication::KEY_S = 's';
const unsigned char WindowApplication::KEY_D = 'd';
const unsigned char WindowApplication::KEY_Q = 'q';
const unsigned char WindowApplication::KEY_E = 'e';
const unsigned char WindowApplication::KEY_Wu = 'W';
const unsigned char WindowApplication::KEY_Au = 'A';
const unsigned char WindowApplication::KEY_Su = 'S';
const unsigned char WindowApplication::KEY_Du = 'D';
const unsigned char WindowApplication::KEY_Qu = 'Q';
const unsigned char WindowApplication::KEY_Eu = 'E';

const int WindowApplication::MOUSE_LEFT_BUTTON = 0;
const int WindowApplication::MOUSE_MIDDLE_BUTTON = 1;
const int WindowApplication::MOUSE_RIGHT_BUTTON = 2;
const int WindowApplication::MOUSE_UP = 0;
const int WindowApplication::MOUSE_DOWN = 1;

const int WindowApplication::MODIFIER_CONTROL = MK_CONTROL;
const int WindowApplication::MODIFIER_LBUTTON = MK_LBUTTON;
const int WindowApplication::MODIFIER_MBUTTON = MK_MBUTTON;
const int WindowApplication::MODIFIER_RBUTTON = MK_RBUTTON;
const int WindowApplication::MODIFIER_SHIFT = MK_SHIFT;
//---------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	WindowApplication* pkTheApp =
		(WindowApplication*)Application::TheApplication;

	switch (msg)
	{
		case WM_PAINT:
		{
			PAINTSTRUCT kPS;
			BeginPaint(hWnd,&kPS);
			pkTheApp->OnDisplay();
			EndPaint(hWnd,&kPS);			
			break;
		}
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			break;
		}
		case WM_CHAR:
		{
			unsigned char ucKey = (unsigned char)wParam;
			// quit if the KEY_TERMINATE is pressed
			if (ucKey == pkTheApp->KEY_TERMINATE)
			{
				PostQuitMessage(0);
				return 0;
			}

			// get the cursor position in client coordinates
			POINT kPoint;
			GetCursorPos(&kPoint);
			ScreenToClient(hWnd, &kPoint);
			int iXPos = (int)kPoint.x;
			int iYPos = (int)kPoint.y;

			pkTheApp->OnKeyDown(ucKey, iXPos, iYPos);
			return 0;
		}
		case WM_KEYDOWN:
		{
			int iKey = (int)wParam;

			// get the cursor position in client coordinates
			POINT kPoint;
			GetCursorPos(&kPoint);
			ScreenToClient(hWnd, &kPoint);
			int iXPos = (int)kPoint.x;
			int iYPos = (int)kPoint.y;

			if ((VK_F1 <= iKey && iKey <= VK_F12)
				|| (VK_PRIOR <= iKey && iKey <= VK_DOWN)
				|| (iKey == VK_INSERT) || (iKey == VK_DELETE) )
			{
				pkTheApp->OnSpecialKeyDown(iKey, iXPos, iYPos);
			}

			return 0;
		}
		case WM_KEYUP:
		{
			// Always uppercase.
			int iKey = (int)wParam;
			// get the cursor position in client coordinates
			POINT kPoint;
			GetCursorPos(&kPoint);
			ScreenToClient(hWnd, &kPoint);
			int iXPos = (int)kPoint.x;
			int iYPos = (int)kPoint.y;
			
			if ((VK_F1 <= iKey && iKey <= VK_F12)
				|| (VK_PRIOR <= iKey && iKey <= VK_DOWN)
				|| (iKey == VK_INSERT) || (iKey == VK_DELETE) )
			{
				pkTheApp->OnSpecialKeyUp(iKey, iXPos, iYPos);
			}
			else
			{
				pkTheApp->OnKeyUp((unsigned char)iKey, iXPos, iYPos);
			}
			return 0;
		}
		case WM_LBUTTONDOWN:
		{
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
			pkTheApp->OnMouseClick(WindowApplication::MOUSE_LEFT_BUTTON,
				WindowApplication::MOUSE_DOWN, iXPos, iYPos, PtrToUint(wParam));
			return 0;
		}
		case WM_LBUTTONUP:
		{
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMouseClick(WindowApplication::MOUSE_LEFT_BUTTON,
                WindowApplication::MOUSE_UP,iXPos,iYPos,PtrToUint(wParam));
            return 0;			
		}
        case WM_MBUTTONDOWN:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMouseClick(WindowApplication::MOUSE_MIDDLE_BUTTON,
                WindowApplication::MOUSE_DOWN,iXPos,iYPos,PtrToUint(wParam));
            return 0;
        }
        case WM_MBUTTONUP:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMouseClick(WindowApplication::MOUSE_MIDDLE_BUTTON,
                WindowApplication::MOUSE_UP,iXPos,iYPos,PtrToUint(wParam));
            return 0;
        }
        case WM_RBUTTONDOWN:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMouseClick(WindowApplication::MOUSE_RIGHT_BUTTON,
                WindowApplication::MOUSE_DOWN,iXPos,iYPos,PtrToUint(wParam));
            return 0;
        }
        case WM_RBUTTONUP:
        {
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));
            pkTheApp->OnMouseClick(WindowApplication::MOUSE_RIGHT_BUTTON,
                WindowApplication::MOUSE_UP,iXPos,iYPos,PtrToUint(wParam));
            return 0;
        }
		case WM_MOUSEMOVE:
		{
            int iXPos = int(LOWORD(lParam));
            int iYPos = int(HIWORD(lParam));

			int iButton = -1;
            if (wParam & MK_LBUTTON)
            {
                iButton = WindowApplication::MOUSE_LEFT_BUTTON;
            }
            else if (wParam & MK_MBUTTON)
            {
                iButton = WindowApplication::MOUSE_MIDDLE_BUTTON;
            }
            else if (wParam & MK_RBUTTON)
            {
                iButton = WindowApplication::MOUSE_RIGHT_BUTTON;
            }

			if (iButton >= 0)
			{
				pkTheApp->OnMotion(iButton, iXPos, iYPos, PtrToUint(wParam));
			}
			else
			{
				pkTheApp->OnPassiveMotion(iXPos, iYPos);
			}

			return 0;
		}
		case WM_SIZE:
		{
			int iWidth = int(LOWORD(lParam));
			int iHeight = int(HIWORD(lParam));	
			pkTheApp->OnResize(iWidth,iHeight);
			break;
		}
	}
	return (DefWindowProc(hWnd, msg, wParam, lParam));
}

//---------------------------------------------------------------------------------------------------
int WindowApplication::Main()
{
	WindowApplication* pkTheApp = (WindowApplication*)TheApplication;
	pkTheApp->KEY_TERMINATE = WindowApplication::KEY_ESCAPE;

	WNDCLASS wc;
	const tCHAR acWinClassName[] = TC("Ripple Engine");
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hbrBackground	= 0;(HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor			= LoadCursor (NULL, IDC_ARROW);
	wc.hIcon			= LoadIcon (NULL, IDI_APPLICATION);
	wc.hInstance		= 0;
	wc.lpfnWndProc		= WndProc;
	wc.lpszClassName	= acWinClassName;
	wc.lpszMenuName		= NULL;
	wc.style			= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	if (!RegisterClass(&wc))
	{
		MessageBox (NULL,TC("Error: Cannot Register Class"),TC("ERROR!"),MB_OK);
		return (0);
	}

	// Require the window to have the specified client area.
	RECT kRect = { 0, 0, pkTheApp->m_iWidth - 1, pkTheApp->m_iHeight - 1};
	AdjustWindowRect(&kRect,WS_OVERLAPPEDWINDOW,false);

	HWND hWindow = CreateWindow(acWinClassName, pkTheApp->m_acWindowTitle, WS_OVERLAPPEDWINDOW,
							pkTheApp->m_iXPosition, pkTheApp->m_iYPosition, kRect.right - kRect.left + 1,
							kRect.bottom - kRect.top + 1, 0, 0, 0, 0);	
	if (hWindow == NULL) 
	{
		MessageBox (NULL,TC("Error: Failed to Create Window"),TC("ERROR!"),MB_OK);
		return (0);
	}	

	// Initialize the renderer
	m_pkWglRenderer = new WglRenderer(hWindow, pkTheApp->m_iWidth, pkTheApp->m_iHeight);
	
	pkTheApp->InitRenderStates();

	// Show window
	ShowWindow(hWindow,SW_SHOW);
	UpdateWindow(hWindow);
	
	// Get timer resolution

	// Message loop
	if(pkTheApp->OnInitialize())
	{
		while (1)
		{
			MSG	msg;
			if (PeekMessage (&msg, 0, 0, 0, PM_REMOVE))
			{
				if (msg.message == WM_QUIT) break;
				TranslateMessage(&msg);
				DispatchMessage (&msg);
			}
			else
			{
				pkTheApp->OnIdle();
			}

			//FrameRateControl(60);
		}
	}
	
	m_pkWglRenderer->Release();
	return (1);
}